package com.liu.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liu.conest.LogType;
import com.liu.config.RedisConfig;
import com.liu.config.SystemConfig;
import com.liu.dao.UserDao;
import com.liu.dao.UserLogDao;
import com.liu.entity.User;
import com.liu.entity.UserLog;
import com.liu.service.intf.UserService;
import com.liu.utils.JedisUtils;
import com.liu.utils.PassUtils;
import com.liu.utils.StringUtils;
import com.liu.vo.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * ━━━━━━神兽出没━━━━━━
 * 　　　┏┓　　　┏┓
 * 　　┏┛┻━━━┛┻┓
 * 　　┃　　　　　　 ┃
 * 　　┃　　　━　　　┃
 * 　　┃　┳┛　┗┳　  ┃
 * 　　┃　　　　　　 ┃
 * 　　┃　　　┻　　　┃
 * 　　┃　　　　　　　┃
 * 　　┗━┓　　　┏━┛Code is far away from bug with the animal protecting
 * 　　　　┃　　　┃    神兽保佑,代码无bug
 * 　　　　┃　　　┃
 * 　　　　┃　　　┗━━━┓
 * 　　　　┃　　　　　　　┣┓
 * 　　　　┃　　　　　　　┏┛
 * 　　　　┗┓┓┏━┳┓┏┛
 * 　　　　　┃┫┫　┃┫┫
 * 　　　　　┗┻┛　┗┻┛
 * ━━━━━━永无BUG!━━━━━━
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao dao;

    @Autowired
    private UserLogDao logDao;

    @Value("${user.passkey}")
    private String key;

    //唯一登录：一个账号，在线1个，如果是第一次，那么自动注册，手机号+验证码登录，验证码一次性
    @Override
    @Transactional //事务
    public R login(String phone, int code, String ip) {
        //1.校验参数
        if (StringUtils.isNoyEmpty(phone) && code > 0 && phone.matches(SystemConfig.Phone_REG)) {

            //2.查询是否存在短信验证码
            if (JedisUtils.esists(RedisConfig.SMS_CODE_LOGIN + phone)) {

                //3.验证 验证码 是否正确
                if (code == Integer.parseInt(JedisUtils.getStr(RedisConfig.SMS_CODE_LOGIN + phone))) {

                    //4.验证当前手机号是否已经注册,不存在，需要自动注册
                    //查询用户
                    User user = dao.selectOne(new QueryWrapper<User>().eq("phone", phone));
                    //验证是否存在当前账号
                    if (user == null) {

                        //5.当前登录的手机号，不存在，需要自动注册
                        user = new User();
                        user.setCtime(new Date());
                        user.setFlag(1);//魔法值
                        user.setPhone(phone);
                        dao.insert(user);
                        logDao.insert(new UserLog(user.getId(), LogType.注册.getValue(), "手机号验证码登录，实现自动注册", ip, new Date()));
                        //检测账号是否被拉黑
                    } else if (user.getFlag() == SystemConfig.USER_ACCOUNT_BLOCK) {
                        return R.fail("该账号已被拉黑,请联系管理员");
                    }
                    //6.唯一登录，一个号只能在线1次，校验 当前手机号是否已经在线
                    if (JedisUtils.esists(RedisConfig.LOGIN_PHONE + phone)) {

                        //存在手机号，说明已经在线，挤掉
                        //7.生成挤掉消息（如果 是App 需要使用第三方推送工具，比如个推、极光等等），存储挤掉的令牌
                        JedisUtils.setSet(RedisConfig.LOGIN_LOSE, JedisUtils.getStr(RedisConfig.LOGIN_PHONE + phone));
                        //清理原来的令牌
                        JedisUtils.del(RedisConfig.LOGIN_TOKEN + JedisUtils.getStr(RedisConfig.LOGIN_PHONE + phone));
                    }

                    //8.生成令牌 AES算法，对手机号和当前的时间戳 进行加密-密文就是令牌
                    String token = PassUtils.aesenc(key, phone + "_" + System.currentTimeMillis());

                    //9.令牌存储到  有状态登录 Redis(1.快 2有效期) 什么类型：String 有效期：30分钟

                    //记录本次的令牌和对应的用户数据
                    JedisUtils.setStr(RedisConfig.LOGIN_TOKEN + token, JSON.toJSONString(user), RedisConfig.LOGIN_TOKEN_TIME);
                    //记录手机号对应的，令牌 目的：验证唯一登录
                    JedisUtils.setStr(RedisConfig.LOGIN_PHONE + phone, token, RedisConfig.LOGIN_TOKEN_TIME);

                    //10.删除手机号验证码 验证码的一次性
                    JedisUtils.del(RedisConfig.SMS_CODE_LOGIN + phone);

                    //11.记录登录日志
                    logDao.insert(new UserLog(user.getId(), LogType.登录.getValue(), "手机号验证码完成登录", ip, new Date()));

                    //12.返回 令牌返回
                    return R.ok(token);
                }
            }
        }
        return R.fail("亲，网络故障");
    }

    //通过令牌查询用户信息
    @Override
    public R query(String token) {
        //1.校验
        if (StringUtils.isNoyEmpty(token)) {
            //2.验证令牌是否有效
            if (JedisUtils.esists(RedisConfig.LOGIN_TOKEN + token)) {
                //3.查询令牌对应的用户信息
                User user = JSON.parseObject(JedisUtils.getStr(RedisConfig.LOGIN_TOKEN + token), User.class);
                user.setPassword("");//敏感数据 脱敏
                String phone = user.getPhone();
                user.setPhone(phone.substring(0, 3) + "****" + phone.substring(7));
                return R.ok(user);
            }
        }
        return R.fail("无效令牌！");
    }

    //校验令牌是否有效
    @Override
    public R checkToken(String token) {
        //1.校验参数
        if (StringUtils.isNoyEmpty(token)) {
            //2.验证当前令牌 是否被挤掉
            if (JedisUtils.check(RedisConfig.LOGIN_LOSE, token)) {
                //删除令牌，挤掉信息只推送
                JedisUtils.delSet(RedisConfig.LOGIN_LOSE, token);
                //如果当前令牌被挤掉
                return R.fail("亲，你的账号已在其地方，登录！");
            } else {//没有被挤掉，需要校验令牌有没有失效
                //3.验证令牌是否有效
                if (JedisUtils.esists(RedisConfig.LOGIN_TOKEN + token)) {
                    //4.令牌有效
                    return R.ok();
                } else {
                    return R.fail("亲，令牌失效，请重新登录！");
                }
            }
        }
        return R.fail("无效令牌！");
    }

    //退出登录
    @Override
    @Transactional
    public R loginOut(String token) {
        //1.校验
        if (StringUtils.isNoyEmpty(token)) {
            //2.验证令牌是否有效
            if (JedisUtils.esists(RedisConfig.LOGIN_TOKEN + token)) {
                //存在，有效
                //通过令牌，获取用户信息
                User user = JSON.parseObject(JedisUtils.getStr(RedisConfig.LOGIN_TOKEN + token), User.class);
                //删除令牌和对应的手机号 Reds中
                JedisUtils.del(RedisConfig.LOGIN_TOKEN + token, RedisConfig.LOGIN_PHONE + user.getPhone());
                return R.ok();
            }
        }
        return R.fail("无效令牌！");
    }

    @Transactional
    @Override
    public R phoneRegister(String phone, int code, String ip) {
        //入参检测
        if (StringUtils.isNoyEmpty(phone) && phone.matches(SystemConfig.Phone_REG) && code > 0) {
            //查询redis是否存在该验证码
            if (JedisUtils.esists(RedisConfig.SMS_CODE_REGISTER + phone)) {
                if (Integer.parseInt(JedisUtils.getStr(RedisConfig.SMS_CODE_REGISTER+phone)) == code) {
                    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("phone", phone);
                    User user = dao.selectOne(queryWrapper);
                    //删除验证码,保证只能使用一次
                    JedisUtils.del(RedisConfig.SMS_CODE_REGISTER + phone);
                    if (user == null) {
                        user = new User(phone, SystemConfig.USER_ACCOUNT_Ok, new Date());
                        dao.insert(user);
                        logDao.insert(new UserLog(user.getId()
                                , LogType.注册.getValue(), "用户手机注册成功", ip, new Date()));
                        return R.ok("注册成功");
                    } else {
                        return R.fail("亲,该账号已经注册过了");
                    }
                } else {
                    return R.ok("验证码不正确,请重新验证");
                }
            } else {
                return R.ok("验证码已过期,请重新验证");
            }
        }
        return R.fail("手机号或验证码不正确");
    }
    @Transactional
    @Override
    public R findPass(String phone, int code, String password, String ip) {
        //入参检测
        if (StringUtils.isNoyEmpty(phone) && phone.matches(SystemConfig.Phone_REG) && code > 0) {
            //验证码存在
            if (JedisUtils.esists(RedisConfig.SMS_CODE_FIND + phone)) {
                //验证码正确
                if (Integer.parseInt(JedisUtils.getStr(RedisConfig.SMS_CODE_FIND + phone)) == code) {
                    //删除验证码
                    JedisUtils.del(RedisConfig.SMS_CODE_FIND + phone);
                    User user = dao.selectOne(new QueryWrapper<User>().eq("phone", phone));
                    if (user == null) {
                        return R.fail("手机号没有注册");
                    } else {
                        if (user.getFlag() == SystemConfig.USER_ACCOUNT_BLOCK){
                            return R.fail("该账号已被拉黑,请联系管理员!");
                        }
                        user.setPassword(password);
                        int update = dao.update(user,new QueryWrapper<User>().eq("id",user.getId()));
                        if (update==1){
                            logDao.insert(new UserLog(user.getId(), LogType.修改密码.getValue(), "用户修改 密码", ip, new Date()));
                            return R.ok("找回密码成功!");
                        }
                    }
                }else{
                    return  R.fail("验证码不正确,请重新验证");
                }
            }else{
                return R.fail("验证码已过期");
            }

        }
        return R.fail("手机号或验证码不正确");
    }
}
